<?php

namespace App\Admin\Controllers;

use App\Admin\Filters\WorshipCollectionFilter;
use App\Admin\Models\WorshipCollection;
use App\Admin\Requests\WorshipCollectionRequest;
use App\Admin\Resources\WorshipCollectionResource;
use App\Admin\Models\WorshipHymn;
use App\Admin\Models\Config;
use App\Admin\Models\SystemMedia;
use App\Admin\Models\SystemMediaCategory;
use App\Admin\Models\SystemModel;
use PhpOffice\PhpPresentation\IOFactory;
use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\DocumentLayout;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Shape\Hyperlink;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Slide\Background\Image;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Cache;

class WorshipCollectionController extends Controller
{
    // PPTX文件名称
    private $pptxName;

    // 存储导出PPTX的分类名称
    private $pptxCate = '赞美诗';

    // 存储导出PPTX的目录
    private $pptxPath = 'uploads/';

    private $configs;

    public function __construct(){
        $this->pptxName = date('Y年m月d日-H时i分s秒').'.pptx';
        $this->configs = array_column(Config::getByCategorySlug('hymn_export')->toArray(), 'value', 'slug');
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 歌单列表
	* @description 获取歌单列表的接口
	* @method GET
	* @url /admin-api/worship-collections
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
	* @remark 这里是备注信息
	* @number 99
	*/
    public function index(WorshipCollectionFilter $filter,  WorshipCollection $worshipCollection)
    {
        $worshipCollections = WorshipCollection::withTrashed()
            ->filter($filter)
            ->select(SystemModel::getFields($worshipCollection->getTable()))
            ->orderBy('updated_at', 'desc')
            ->paginate();

        return $this->ok(WorshipCollectionResource::collection($worshipCollections));
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 添加歌单页面参数
	* @description 获取添加歌单页面参数的接口
	* @method GET
	* @url /admin-api/worship-collections/create
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
	* @remark 这里是备注信息
	* @number 99
	*/
    public function create()
    {
        return $this->ok();
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 添加歌单
	* @description 添加歌单的接口
	* @method POST
	* @url /admin-api/worship-collections
	* @json_param {}
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
    * @remark 这里是备注信息
	* @number 99
	*/
    public function store(WorshipCollectionRequest $request, WorshipCollection $worshipCollection)
    {
        $inputs = $request->validated();
        $token = md5(json_encode($inputs));
        unset($inputs['token']);
        if (Cache::get($token) == 2) {
            return $this->error('请不要重复提交数据');
        } 
        Cache::put($token, 1);
        $worshipCollection = $worshipCollection->add($inputs);
        Cache::put($token, 2);

        return $this->created(WorshipCollectionResource::make($worshipCollection));
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 歌单详情
	* @description 获取歌单详情的接口
	* @method GET
	* @url /admin-api/worship-collections/{id}/edit
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
    * @remark 这里是备注信息
	* @number 99
	*/
    public function edit(Request $request, WorshipCollection $worshipCollection)
    {
        return $this->ok(WorshipCollectionResource::make($worshipCollection));
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 编辑歌单
	* @description 编辑歌单的接口
	* @method PUT
	* @url /admin-api/worship-collections/{id}
	* @json_param {}
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
    * @remark 这里是备注信息
	* @number 99
	*/
    public function update(WorshipCollectionRequest $request, WorshipCollection $worshipCollection)
    {
        $inputs = $request->validated();
        $worshipCollection->update($inputs);

        return $this->created(WorshipCollectionResource::make($worshipCollection));
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 删除歌单
	* @description 删除歌单的接口
	* @method DELETE
	* @url /admin-api/worship-collections/{id}
    * @param 参数名 是否必选 类型 说明
	* @return {}
	* @return_param 参数名 类型 说明
    * @remark 这里是备注信息
	* @number 99
	*/
    public function destroy(WorshipCollection $worshipCollection)
    {
        $worshipCollection->delete();
        return $this->noContent();
    }

    // 创建目录PPTX
    public function createMenuPptx($objPHPPresentation, $list){
        // 赞美诗按字母A-Z排序
        $list = letterSort($list, 'name');
        // 把字母和空行作为一条数据放入目录中
        $nameList = [];
        foreach ($list as $key => $value) {
            // 添加字母
            array_push($nameList, [
                'index'=>$key,
                'name'=>$key,
                'slide_number'=>false
            ]);
            // 遍历添加每个字母对应的赞美诗
            foreach ($value as $key2 => $value2) {
                array_push($nameList, [
                    'index'=>$key2,
                    'name'=>$value2['name'],
                    'slide_number'=>$value2['slide_number']
                ]);
            }
            // 添加空行
            array_push($nameList, [
                'index'=>$key,
                'name'=>'',
                'slide_number'=>false
            ]);
        }
        // 总条数
        $nameCount = count($nameList);
        // 获取分页信息
        $pageInfo = getPage(1, $nameCount, $this->configs['menu_song_max_numbers']);
        for ($i=1; $i <= $pageInfo['totalPages']; $i++) {
            $cPageInfo = getPage($i, $nameCount, $this->configs['menu_song_max_numbers']);
            $cNameList = array_slice($nameList, $cPageInfo['firstRow'], $cPageInfo['listRows'], true);
            $cNameCount = count($cNameList);
            if ($i==1) {
                // 创建目录的第一张幻灯片，使用getActiveSlide()
                $cSlide = $objPHPPresentation->getActiveSlide();
            }else{
                $cSlide = $objPHPPresentation->createSlide();
            }
            $cSlide->setSlideLayout($this->slideLayout);
            // 循环创建目录页的每一列歌曲
            for ($j=1; $j <= $this->configs['menu_slide_max_numbers']; $j++) { 
                $columnPageInfo = getPage($j, $cNameCount, $this->configs['menu_song_max_numbers']/$this->configs['menu_slide_max_numbers']);
                // 创建目录文本域
                $cShape = $cSlide->createRichTextShape()
                    ->setWidthAndHeight(300,480)
                    ->setOffsetX(30+(300*($j-1)))
                    ->setOffsetY(15);
                // 目录文本水平居左
                $cShape->getActiveParagraph()
                    ->getAlignment()
                    ->setHorizontal(Alignment::HORIZONTAL_LEFT);
                //设置行高
                $cShape->getActiveParagraph()->setLineSpacing(150);
                $cSongList = array_slice($cNameList, $columnPageInfo['firstRow'], $columnPageInfo['listRows'], true);
                foreach ($cSongList as $key => $value) {
                    if ($value['name'] && $value['slide_number']) {
                        // 矩形形状填充文本
                        $cTextRun = $cShape->createTextRun(sprintf("%02d",$value['index']+1).' '.$value['name']);
                        // 目录设置字体属性
                        $cTextRun->getFont()
                            ->setName('微软雅黑')
                            ->setSize($this->configs['menu_font_zize']);
                        $clink = new Hyperlink();
                        $clink->setSlideNumber($value['slide_number']);
                        $cTextRun->setHyperlink($clink);
                    } elseif ($value['name'] && !$value['slide_number']) {
                        // 矩形形状填充文本
                        $cTextRun = $cShape->createTextRun($value['name']);
                        // 目录设置字体属性
                        $cTextRun->getFont()
                            ->setName('微软雅黑')
                            ->setBold(true)
                            ->setSize($this->configs['menu_font_zize']);
                    } else {
                        $cTextRun = $cShape->createTextRun(" ");
                        $cTextRun->getFont()->setName('微软雅黑')->setSize($this->configs['menu_font_zize']);
                    }
                    $cShape->createBreak();
                }
                $cShape = null;
            }
        }
        return $objPHPPresentation;
    }

    // 创建PPTX
    public function createPptx($list){
        $objPHPPresentation = $this->createMenuPptx($this->setMasterSlide(), $list);
        foreach ($list as $key => $value) {
            $contents = array_filter(explode("#", $value['content']));
            $contentCount = count($contents);
            // 遍历创建诗歌的剩余幻灯片
            for ($i=0; $i < $contentCount; $i++) {
                if (!$contents[$i]) {
                    continue;
                }
                $nextSlide = $objPHPPresentation->createSlide();
                $nextSlide->setSlideLayout($this->slideLayout);
                
                // 创建页码形状
                $pageShape = $nextSlide->createRichTextShape()
                    ->setWidthAndHeight(60,20)
                    ->setOffsetX(890)
                    ->setOffsetY(20);
                // 页码
                $pageTextRun = $pageShape->createTextRun(($i+1).'/'.$contentCount);
                // 设置页码字体属性
                $pageTextRun->getFont()
                    ->setName('微软雅黑')
                    ->setSize(13);
                // 创建矩形形状
                $nextShape = $nextSlide->createRichTextShape()
                    ->setWidthAndHeight(960,450)
                    ->setOffsetY(35);
                // 文本形状布局调整
                $nextShape->getActiveParagraph()
                    ->getAlignment()
                    ->setHorizontal(Alignment::HORIZONTAL_CENTER);
                // 设置行高
                $nextShape->getActiveParagraph()->setLineSpacing(120);
                if ($i==0) {
                    // 第一行添加歌名
                    $firstTextRun = $nextShape->createTextRun($value['name']);
                    // 歌名设置字体属性
                    $firstTextRun->getFont()
                        ->setName('微软雅黑')
                        ->setBold(true)
                        ->setSize($this->configs['name_font_size']);
                    $nextShape->createBreak();
                    $firstTextRun = $nextShape->createTextRun(" ");
                    $firstTextRun->getFont()->setName('微软雅黑')->setSize(15);
                    $nextShape->createBreak();
                }
                $nextWordList = explode("\n", $contents[$i]);
                foreach ($nextWordList as $key3 => $value3) {
                    $value3 = str_replace("\n","",$value3);
                    if ($value3) {
                        // 矩形形状填充文本
                        $nextTextRun = $nextShape->createTextRun($value3);
                        // 文本设置字体属性
                        $nextTextRun->getFont()
                            ->setName('微软雅黑')
                            ->setSize($this->configs['content_font_size']);
                    } else {
                        $nextTextRun = $nextShape->createTextRun(" ");
                        $nextTextRun->getFont()->setName('微软雅黑')->setSize(10);
                    }
                    if ($key3 < count($nextWordList)-1) {
                        $nextShape->createBreak();
                    }
                }

                $shapeDrawing = $nextSlide->createDrawingShape();
                $shapeDrawing->setName('home')
                    ->setDescription('link home')
                    ->setPath('pptx/images/home.png')
                    ->setHeight(36)
                    ->setOffsetX(20)
                    ->setOffsetY(20)
                    ->getHyperlink()->setSlideNumber();
                $shapeDrawing->getShadow()->setVisible(true)
                    ->setDirection(45)
                    ->setDistance(2);
            } 
        }  
        $oWriterPPTX = IOFactory::createWriter($objPHPPresentation, 'PowerPoint2007');
        $savePath = $this->pptxPath.$this->pptxName;
        $oWriterPPTX->save($savePath);  
        return $savePath;
    }

    // 设置幻灯片母版
    public function setMasterSlide(){
        $objPHPPresentation = new PhpPresentation();
        // 创建母版布局
        $masterSlide = $objPHPPresentation->getAllMasterSlides()[0];
        // 幻灯片填充背景图
        $image = new Image();
        $pathInfo = parse_url($this->configs['slide_background']['path']);
        if (isset($pathInfo['host'])) {
            file_put_contents(ltrim($pathInfo['path'], '/'), file_get_contents($this->configs['slide_background']['path']));
        }
        $image->setPath(ltrim($pathInfo['path'], '/'));
        $masterSlide->setBackground($image);
        // 将幻灯片母版的形状集合应用于幻灯片版式
        $layout = new DocumentLayout();
        $layout->setDocumentLayout('screen'.$this->configs['slide_ratio']); // 长宽比
        $objPHPPresentation->setLayout($layout);
        $this->slideLayout = $masterSlide->getAllSlideLayouts()[0];
        $this->slideLayout->setShapeCollection($masterSlide->getShapeCollection());
        return $objPHPPresentation;
    }

    /**
	* showdoc
	* @catalog 接口简介/歌单相关
	* @title 导出歌单
	* @description 导出歌单的接口
	* @method POST
	* @url /admin-api/worship-collections/export/{id}
    * @json_param {}
    * @param 参数名 是否必选 类型 说明
	* @return {"pptx_url": "http://hymn.com/uploads/worship-hymns/2021年07月30-18时14分19秒.pptx"}
	* @return_param pptx_url string 导出的PPT链接 
    * @remark 这里是备注信息
	* @number 99
	*/
    public function export(
        Request $request, 
        WorshipCollection $worshipCollection,
        Int $id
    ){   
        // 查询歌单中的赞美诗ID
        $hymnIds = $worshipCollection->where('id', '=', $id)->value('worship_hymns');
        // 查询赞美诗
        $worshipHymns = WorshipHymn::query()
            ->whereIn('id', $hymnIds)
            ->get([
                'id',
                'content',
                'name'
            ])
            ->toArray();
       
        // 赞美诗总数
        $worshipHymnCount = count($worshipHymns);
        // 计算带上字母和空行的总数
        $letters = [];
        foreach ($worshipHymns as $key => $value) {
            $name = preg_replace('/^\d+/','',$value['name']);
            // 返回取汉字的第一个字的首字母
            $letter = getFirstLetter($name);
            if (!in_array($letter, $letters)) {
                // 目录页显示字母和空行，这里需要+2
                $worshipHymnCount+=2;
                array_push($letters, $letter);
            }
        }

        // 获取歌单导出设置中的目录页排序规则
        $menuSongSrderRules = explode('\n', $this->configs['menu_song_order_rules']);
       
        if ($menuSongSrderRules) {
            // 按照目录页排序规则排序
            $worshipHymns = orderByArray(array_column($worshipHymns, null, 'name'), $menuSongSrderRules);
        } else {
            // 按歌名首字母排序
            $order = 'name_letter';
            $worshipHymns = array_key_sort($worshipHymns, $order, SORT_ASC, SORT_STRING);
        }
       
        // 获取目录的分页信息
        $pageInfo = getPage(1, $worshipHymnCount, $this->configs['menu_song_max_numbers']);
        // 遍历计算每首歌对应的幻灯片索引，
        // 首先确定第一首歌对应的幻灯片索引=目录总页数+1
        $slideNumber = $pageInfo['totalPages']+1;

        $worshipHymns[0]['slide_number'] = $slideNumber;
        for ($i=1; $i < $worshipHymnCount; $i++) {
            if (isset($worshipHymns[$i])) {
                $worshipHymns[$i]['slide_number'] = $worshipHymns[$i-1]['slide_number']+count(array_filter(explode("#", $worshipHymns[$i-1]['content'])));
            }
        }
        // 创建PPT
        $path = $this->createPptx($worshipHymns);
        // 创建成功后的PPT链接
        $pptxUrl = config()->get('app.url').'/'.$path;

        // 存储到数据库
        DB::beginTransaction();
        // 更新或创建文件分类
        $cateData['name'] = $this->pptxCate;
        $cateData['parent_id'] = 0;
        $cate = SystemMediaCategory::updateOrCreate($cateData);
        // 获取文件信息
        $file = UploadedFile::fake()->image($path);
        // 存储文件信息
        $mediaData['origin_filename'] = $this->pptxName;
        $mediaData['filename'] = md5_file($file);
        $mediaData['ext'] = 'pptx';
        $mediaData['category_id'] = $cate->id;
        $mediaData['path'] = '/'.$path;
        $mediaData['path_key'] = md5($path);
        $mediaData['size'] = $file->getSize();
        $mediaData['mime_type'] = $file->getMimeType();
        $media = SystemMedia::create($mediaData);
        if ($cate && $media) {
            DB::commit();
        } else {
            DB::rollBack();
        }
        return $this->ok(['pptx_url'=>$pptxUrl]);
    }

    public function show(Request $request, Int $id)
    {
        $worshipCollection = WorshipCollection::withTrashed()
            ->select(SystemModel::getFields(app(WorshipCollection::class)->getTable(), 'detail'))
            ->find($id);
        return $this->ok(WorshipCollectionResource::make($worshipCollection));
    }
}
